#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/mm.h>
#include <linux/signal.h>
#include <linux/err.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/cma.h>
#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_reserved_mem.h>
#include <linux/dma-mapping.h>
#include <linux/cma.h>
#include <linux/dma-contiguous.h>

//#define NO_DMA_ALLOC_CPY

#define DEVICE_NAME "dma_driver"
#define AXI_HP0_WRITE_ADDR 0x27FFE000
#define MM_SIZE (4 * 1024)

void dma_callback_func(void *dma_async_param);
void my_dma_copy(u32 dma_dst, u32 dma_src, int size);
void dma_init(struct platform_device *pdev, int size);
void dma_del(void);

struct cma;

struct dma_chan *chan;
dma_addr_t dma_src;
dma_addr_t dma_dst;
char *src = NULL;
char *dst = NULL;
struct dma_device *dev;
struct dma_async_tx_descriptor *tx = NULL;
enum dma_ctrl_flags flags;
dma_cookie_t cookie;

extern struct cma *dma_contiguous_default_area;

// 当 DMA 传输完成时，此函数将被调用
void dma_callback_func(void *dma_async_param)
{
    int i = 0;

    printk("[%s][%d] 1441================\r\n", __func__, __LINE__);

    for (i = 0; i < MM_SIZE; i++) {
        if (*(dst + i) != (char)('a' + i % 26)) {
            printk("Failed\n");
            return;
        }
    }

    printk("[%s][%d] 33333================\r\n", __func__, __LINE__);
    printk("PASS ===== 1111\n");
}

void my_dma_copy(u32 dma_dst, u32 dma_src, int size)
{
    int status;
    int i;

    // 确保数据在开始 DMA 传输之前对设备可见
    // dma_sync_single_for_device(dev->dev, dma_src, size, DMA_BIDIRECTIONAL);

    printk("[%s][%d] ===\r\n", __func__, __LINE__);

    // 使用 chan->device->device_prep_dma_memcpy 准备 DMA 描述符
    tx = chan->device->device_prep_dma_memcpy(chan, dma_dst, dma_src, size, flags);
    if (!tx) {
        printk(KERN_INFO "Failed to prepare DMA memcpy");
        return;
    }

    printk("[%s][%d] ===\r\n", __func__, __LINE__);

    tx->callback = dma_callback_func;
    tx->callback_param = NULL;
    cookie = tx->tx_submit(tx);
    if (dma_submit_error(cookie)) {
        printk(KERN_INFO "Failed to do DMA tx_submit");
        return;
    }

    printk("[%s][%d] ===\r\n", __func__, __LINE__);
    dma_async_issue_pending(chan);
    status = dma_wait_for_async_tx(tx);

    printk("[%s][%d] ===\r\n", __func__, __LINE__);
    if (status != DMA_COMPLETE) {
        printk("Failed to complete DMA transfer\n");
        return;
    }

    // 确保数据在 DMA 传输后对 CPU 可见
   // dma_sync_single_for_cpu(dev->dev, dma_dst, size, DMA_BIDIRECTIONAL);

    if (memcmp((void *)dst, (void *)src, size) == 0) {
        printk("Data transfer and verification succeeded\n");
    } else {
        printk("Data transfer verification failed\n");
    }

    for (i = 0; i < size; i++) {
        if (*(dst + i) != (char)('a' + i % 26)) {
            printk("Failed\n");
            return;
        }
    }

    printk("PASS ==================22222\n");
}

void dma_init(struct platform_device *pdev, int size)
{
    dma_cap_mask_t mask;
    struct device *mydev = &pdev->dev;

    printk("[%s][%d] ===\r\n", __func__, __LINE__);
    // 设置设备的 DMA 掩码
    if (dma_set_mask(mydev, DMA_BIT_MASK(64))) {
        printk(KERN_ERR "Failed to set DMA mask\n");
        return;
    }

    printk("[%s][%d] ===\r\n", __func__, __LINE__);
    // 设置设备的一致性 DMA 掩码
    if (dma_set_coherent_mask(mydev, DMA_BIT_MASK(64))) {
        printk(KERN_ERR "Failed to set coherent DMA mask\n");
        return;
    }

    printk("[%s][%d] before dma_alloc_coherent src ===\r\n", __func__, __LINE__);
    src = dma_alloc_coherent(mydev, size, &dma_src, GFP_KERNEL);
    if (src == NULL) {
        printk(KERN_INFO "src = NULL! ============== \r\n");
        return;
    }

#ifdef NO_DMA_ALLOC_CPY
    printk("[%s][%d] after dma_alloc_coherent src ===\r\n", __func__, __LINE__);

    dst = ioremap(AXI_HP0_WRITE_ADDR, size);
    //dst = phys_to_virt(AXI_HP0_WRITE_ADDR);
    if (dst == NULL) {
        printk(KERN_INFO "dst = NULL! ============== \r\n");
        return;
    }

    dma_dst = AXI_HP0_WRITE_ADDR;
   
    printk("[%s][%d] ===\r\n", __func__, __LINE__);

#else

    printk("[%s][%d] after dma_alloc_coherent src ===\r\n", __func__, __LINE__);
    dst = dma_alloc_coherent(mydev, size, &dma_dst, GFP_KERNEL);
    if (dst == NULL) {
        printk(KERN_INFO "src = NULL! ============== \r\n");
        return;
    }
#endif

    dma_cap_zero(mask);
    //dma_cap_set(DMA_SLAVE, mask);
    dma_cap_set(DMA_MEMCPY, mask);

    chan = dma_request_channel(mask, NULL, NULL);
    if (chan == NULL) {
        printk(KERN_INFO "chan = NULL! ============== \r\n");
        return;
    }

    flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
    dev = chan->device;
}

void dma_del(void)
{
    if (dst) {
        iounmap(dst);
    }

    if (src) {
        dma_free_coherent(dev->dev, MM_SIZE, src, &dma_src);
    }

    if (chan) {
        dma_release_channel(chan);
    }
}

static int device_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int device_close(struct inode *inode, struct file *file)
{
    printk("device close\n");
    return 0;
}

static ssize_t device_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
    int ret = 0;

    my_dma_copy(dma_dst, dma_src, MM_SIZE);

    return ret;
}

static struct file_operations device_fops = {
    .owner = THIS_MODULE,
    .open = device_open,
    .release = device_close,
    .read = device_read,
};

static struct miscdevice MMAP_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &device_fops,
};

extern struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align,
			      gfp_t gfp_mask);
                  
static int my_probe(struct platform_device *pdev)
{
    int ret = 0;
    int i = 0;

    struct device *mydev = &pdev->dev;

    printk("[%s][%d] 1142 ================\r\n", __func__, __LINE__);
    ret = misc_register(&MMAP_misc);
    if (ret) {
        printk("Error: misc_register failed!\n");
        return ret;
    }

    printk("[%s][%d] ================\r\n", __func__, __LINE__);
    dma_init(pdev, MM_SIZE);

    for (i = 0; i < MM_SIZE; i++) {
        *(src + i) = (char)('a' + i % 26);
    }

    printk("[%s][%d] ================\r\n", __func__, __LINE__);

    my_dma_copy(dma_dst, dma_src, MM_SIZE);

    printk("[%s][%d] ================\r\n", __func__, __LINE__);

    return 0;
}

static int my_remove(struct platform_device *pdev)
{
    dma_del();
    misc_deregister(&MMAP_misc);
    return 0;
}

static const struct of_device_id my_match[] = {
    { .compatible = "my-virtual-device" },
    { /* end of list */ }
};
MODULE_DEVICE_TABLE(of, my_match);

static struct platform_driver my_driver = {
    .probe = my_probe,
    .remove = my_remove,
    .driver = {
        .name = "my-virtual-device",
        .of_match_table = my_match,
    },
};

module_platform_driver(my_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("DMA_test");
